package com.zerocarbon.framework.core.jwt;

import cn.hutool.core.convert.Convert;
import com.alibaba.fastjson.JSON;
import com.zerocarbon.framework.common.bo.TokenBO;
import com.zerocarbon.framework.common.dto.JwtUserDTO;
import com.zerocarbon.framework.common.result.vo.AuthTokenVO;
import com.zerocarbon.framework.common.utils.BeanPlusUtil;
import com.zerocarbon.framework.core.properties.JwtProperties;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.AllArgsConstructor;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

import static com.zerocarbon.framework.common.constants.ContextConstants.*;

/**
 * 认证工具类
 */
@AllArgsConstructor
public class TokenService {
	
    /**
     * 认证服务端使用
     * 生成和 解析token
     */
    private JwtProperties jwtProperties;
    
    /**
     * 创建认证token
     * @param jwtUserDTO
     * @param expireMillis
     * @return
     */
    public AuthTokenVO createAuthToken(JwtUserDTO jwtUserDTO, Long expireMillis) {
    	return this.createAuthToken(jwtUserDTO, expireMillis, jwtProperties.getAlg(), getCreateJwtKey(jwtProperties.getAlg()));
    }

    /**
     * 创建认证token
     * @param jwtUserDTO
     * @param expireMillis
     * @param base64Security
     * @return
     */
    public AuthTokenVO createAuthToken(JwtUserDTO jwtUserDTO, Long expireMillis, SignatureAlgorithm alg, String base64Security) {
        if (expireMillis == null || expireMillis <= 0) {
            expireMillis = jwtProperties.getExpire();
        }
        //设置jwt参数
        Map<String, String> param = new HashMap<>(16);
        param.put(JWT_KEY_TOKEN_TYPE, ACCESS_TOKEN);
        param.put(JWT_KEY_HEADER_PREFIX, BEARER_HEADER_PREFIX);
        param.put(JWT_KEY_USER_ID, jwtUserDTO.getUserId().toString());
        param.put(JWT_KEY_USER_IDENTITY, jwtUserDTO.getUserIdentity().toString());
        param.put(JWT_KEY_ACCOUNT_ID, jwtUserDTO.getAccountId().toString());
        param.put(JWT_KEY_USER_NAME, jwtUserDTO.getUserName());
        param.put(JWT_KEY_TENANT_ID, jwtUserDTO.getTenantId().toString());
        param.put(JWT_KEY_USER_MOBILE, jwtUserDTO.getUserMobile());
        param.put(JWT_KEY_COMPANY_ID, jwtUserDTO.getCompanyId().toString());
        param.put(JWT_KEY_COMPANY_NAME, jwtUserDTO.getCompanyName());
        param.put(JWT_KEY_ROLES, JSON.toJSONString(jwtUserDTO.getRoleIds()));
        param.put(JWT_KEY_PERMS, JSON.toJSONString(jwtUserDTO.getPermIds()));

        String tokenUuid = UUID.randomUUID().toString().replaceAll("-", "");
        TokenBO token = JwtHelper.createJWT(tokenUuid, param, expireMillis, alg, base64Security);			//AccessToken
        TokenBO refreshToken = createRefreshToken(jwtUserDTO, alg, base64Security);							//refreshToken

        AuthTokenVO authInfo = BeanPlusUtil.toBean(jwtUserDTO, AuthTokenVO.class);
        authInfo.setTokenUuid(tokenUuid).setAccessToken(token.getToken()).setTokenType(ACCESS_TOKEN).setHeaderPrefix(BEARER_HEADER_PREFIX)
        .setRefreshToken(refreshToken.getToken()).setRefreshExpiration(refreshToken.getExpiration()).setRefreshExpiresIn(refreshToken.getExpire())
        .setExpiresIn(token.getExpire()).setExpiration(token.getExpiration());
        return authInfo;
    }

    /**
     * 创建refreshToken
     * @param jwtUserDTO 用户信息
     * @return refreshToken
     */
    private TokenBO createRefreshToken(JwtUserDTO jwtUserDTO, SignatureAlgorithm alg, String base64Security) {
        Map<String, String> param = new HashMap<>(16);
        String tokenUuid = UUID.randomUUID().toString().replaceAll("-", "");
        param.put(JWT_KEY_TOKEN_TYPE, REFRESH_TOKEN);
        param.put(JWT_KEY_HEADER_PREFIX, BEARER_HEADER_PREFIX);
        param.put(JWT_KEY_USER_ID, jwtUserDTO.getUserId().toString());
        param.put(JWT_KEY_COMPANY_ID, jwtUserDTO.getCompanyId().toString());
        param.put(JWT_KEY_TENANT_ID, jwtUserDTO.getTenantId().toString());
        return JwtHelper.createJWT(tokenUuid, param, jwtProperties.getRefreshExpire(), alg, base64Security);
    }

    /**
     * 解析token
     * @param token
     * @return 用户信息
     */
    public AuthTokenVO getAuthTokenVO(String token) {
    	return getAuthTokenVO(token, jwtProperties.getAlg(), this.getParseJwtKey(jwtProperties.getAlg()));
    }
    
    /**
     * 解析token
     * @param token
     * @param base64Security 
     * @return 用户信息
     */
    public AuthTokenVO getAuthTokenVO(String token, SignatureAlgorithm alg, String base64Security) {

        Claims claims = JwtHelper.getClaims(token, alg, base64Security);
        String tokenType = Convert.toStr(claims.get(JWT_KEY_TOKEN_TYPE));
        Date expiration = claims.getExpiration();
        Long userId = Convert.toLong(claims.get(JWT_KEY_USER_ID));
        Long accountId = Convert.toLong(claims.get(JWT_KEY_ACCOUNT_ID));
        Integer userIdentity = Convert.toInt(claims.get(JWT_KEY_USER_IDENTITY));
        String userName = Convert.toStr(claims.get(JWT_KEY_USER_NAME));
        Long companyId = Convert.toLong(claims.get(JWT_KEY_COMPANY_ID));
        String companyName = Convert.toStr(claims.get(JWT_KEY_COMPANY_NAME));
        Long tenantId = Convert.toLong(claims.get(JWT_KEY_TENANT_ID));

        return new AuthTokenVO()
                .setAccessToken(token).setTokenType(tokenType).setTokenUuid(claims.getId())
                .setExpiresIn(expiration != null ? expiration.getTime() : 0L).setExpiration(expiration)
                .setAccountId(accountId).setUserId(userId).setUserIdentity(userIdentity).setUserName(userName)
                .setCompanyId(companyId).setCompanyName(companyName)
                .setTenantId(tenantId);
                
    }
    
    private String getCreateJwtKey(SignatureAlgorithm alg) {
    	if(SignatureAlgorithm.RS256.getValue().equals(alg.getValue()))
    		return jwtProperties.getPrivateKey();
    	else
    		return jwtProperties.getJwtSecret(); 
    }
    
    private String getParseJwtKey(SignatureAlgorithm alg) {
    	if(SignatureAlgorithm.RS256.getValue().equals(alg.getValue()))
    		return jwtProperties.getPublicKey();
    	else
    		return jwtProperties.getJwtSecret(); 
    }

    /**
     * 解析刷新token
     * @param refreshToken
     * @return 
     */
    public AuthTokenVO getRefreshAuthTokenVO(String refreshToken) {
    	return getRefreshAuthTokenVO(refreshToken, jwtProperties.getAlg(), this.getParseJwtKey(jwtProperties.getAlg()));
    }
    
    /**
     * 解析刷新token
     * @param refreshToken
     * @param base64Security
     * @return
     */
    public AuthTokenVO getRefreshAuthTokenVO(String refreshToken, SignatureAlgorithm alg, String base64Security) {
        Claims claims = JwtHelper.parseJWT(refreshToken, alg, base64Security);
        String tokenType = Convert.toStr(claims.get(JWT_KEY_TOKEN_TYPE));
        Long userId = Convert.toLong(claims.get(JWT_KEY_USER_ID));
        Long tenantId = Convert.toLong(claims.get(JWT_KEY_TENANT_ID));
        Date expiration = claims.getExpiration();
        return new AuthTokenVO()
                .setRefreshToken(refreshToken).setRefreshExpiration(expiration != null ? expiration : new Date()).setTokenType(tokenType)
                .setUserId(userId).setTenantId(tenantId);
    }
}